#pragma once
#include <map>
#include <string>
#include <fstream>
#include <set>
#include "../Comm/log.hpp"
#include "../Comm/util.hpp"

#include <iostream>
#include <mysql_driver.h>
#include <mysql_connection.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
#include <cppconn/resultset.h>
#include <cppconn/exception.h>

#include <mutex>
#include <memory>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

namespace ns_model
{
    // 首页展示的题目信息
    struct Info
    {
        string _id;    // 编号
        string _title; // 标题
        string _level; // 难度

        bool operator<(const Info &inf) const
        {
            return _id < inf._id;
        }

        Info() {}

        Info(string id, string title, string level)
            : _id(id), _title(title), _level(level)
        {
        }
    };

    // 具体的题目结构
    struct Question : public Info
    {
        unsigned int _cpu_limit; // 时间限制
        unsigned int _mem_limit; // 空间限制

        string _desc;         // 题面描述
        string _show;         // 预先给定的接口代码
        string _to_be_merged; // 待合并的完整测试代码
    };

    // 加载题目信息
    class Model
    {
        // 数据库信息（建议放配置文件，此处方便测试，硬编码）
        const string host = "tcp://ip:port"; // 数据库主机
        const string usr = "";               // 数据库用户
        const string passwd = "";            // 密码
        const string db_name = "";           // 选择使用的具体数据库

        // 确保正常无危害的SQL语句执行(通常是内部确定的)
        unique_ptr<sql::ResultSet> NormalExecuteSQL(sql::SQLString &sql)
        {
            // 创建一个 Statement 对象用于执行 SQL 语句
            std::unique_ptr<sql::Statement> stmt(_con->createStatement());
            if (!stmt)
            {
                LOG(ERROR, "创建SQL语句执行对象失败!");
                return nullptr;
            }

            try
            {
                std::unique_ptr<sql::ResultSet> ret(stmt->executeQuery(sql));
                return ret;
            }
            catch (sql::SQLException &e)
            {
                LOG(ERROR, "执行 SQL 语句时出错: %s", e.what());
                if (!strcmp(e.what(), "Lost connection to MySQL server during query"))
                {
                    Connect();
                }
                return nullptr;
            }
        }

        // 做安全性检查的SQL执行
        unique_ptr<sql::ResultSet> PrepareExecuteSQL(string id, sql::SQLString &SQL)
        {
            unique_ptr<sql::PreparedStatement> pstmt(_con->prepareStatement(SQL));
            if (!pstmt)
            {

                LOG(ERROR, "创建SQL语句执行对象失败!");
                return nullptr;
            }
            try
            {
                pstmt->setString(1, id);
                unique_ptr<sql::ResultSet> rst(pstmt->executeQuery());
                return rst;
            }
            catch (sql::SQLException &e)
            {
                LOG(ERROR, "执行 SQL 语句时出错: %s", e.what());
                if (!strcmp(e.what(), "Lost connection to MySQL server during query"))
                {
                    Connect();
                }
                return nullptr;
            }
        }

        void Connect()
        {
            sql::mysql::MySQL_Driver *driver = sql::mysql::get_driver_instance();
            if (driver == nullptr)
            {
                LOG(FATAL, "创建MySQL驱动实例失败!");
                exit(-1);
            }
            _con = driver->connect(host, usr, passwd);
            if (_con == nullptr)
            {
                LOG(FATAL, "连接数据库失败!");
                exit(-1);
            }
            LOG(INFO, "连接数据库成功!");

            // 选择要使用的数据库
            _con->setSchema(db_name);
        }

    public:
        Model()
        {
            // 建立连接
            Connect();
        }

        ~Model()
        {
            if (_con)
            {
                delete _con;
            }
        }

        // 获得题目列表
        set<Info> GetList()
        {
            lock_guard<std::mutex> lock(_mtx);
            LOG(INFO, "请求题目列表.");
            sql::SQLString SQL = "select id,title,level from QuestionBank";
            unique_ptr<sql::ResultSet> rst = NormalExecuteSQL(SQL);
            if (!rst)
            {
                return set<Info>();
            }

            set<Info> temp;
            while (rst->next())
            {
                temp.insert({rst->getString("id"), rst->getString("title"), rst->getString("level")});
            }
            return temp;
        }

        // 获得具体题目信息
        /**************************
         * SQL 注入是一种常见的网络攻击手段，攻击者通过在用户输入中插入恶意的 SQL 代码，从而改变原 SQL 语句的语义，
         * 达到非法访问或修改数据库的目的。
         * PreparedStatement（预编译） 通过使用占位符（通常是 ?）来表示参数，
         * 将 SQL 语句的结构和参数分开处理，数据库会对参数进行严格的类型检查和转义，从而避免了恶意 SQL 代码的注入。
         */

        bool GetOneQuestion(string id, Question &qsn)
        {
            lock_guard<std::mutex> lock(_mtx);
            sql::SQLString SQL = "select id,title,level,interface,description from QuestionBank where id = ?";
            unique_ptr<sql::ResultSet> rst = PrepareExecuteSQL(id, SQL);
            if (!rst)
            {
                return false;
            }

            while (rst->next())
            {
                qsn._id = rst->getString("id");
                qsn._title = rst->getString("title");
                qsn._level = rst->getString("level");
                qsn._show = rst->getString("interface");
                qsn._desc = rst->getString("description");
            }
            LOG(INFO, "请求题目 (%d.%s) ", stoi(qsn._id), qsn._title.c_str());
            return true;
        }

        // Get to_be_merged code
        string Get_to_be_merged(string id)
        {
            lock_guard<std::mutex> lock(_mtx);
            sql::SQLString SQL = "select to_be_merged from QuestionBank where id = ?";
            unique_ptr<sql::ResultSet> rst = PrepareExecuteSQL(id, SQL);
            string ret;
            while (rst->next())
            {
                ret = rst->getString("to_be_merged");
            }
            return ret;
        }

        // Get 资源限制
        unsigned int Get_cpu_limit(string id)
        {
            lock_guard<std::mutex> lock(_mtx);
            sql::SQLString SQL = "select cpu_limit from QuestionBank where id = ?";
            unique_ptr<sql::ResultSet> rst = PrepareExecuteSQL(id, SQL);
            unsigned int ret;
            while (rst->next())
            {
                ret = rst->getInt("cpu_limit");
            }
            return ret;
        }

        unsigned int Get_mem_limit(string id)
        {
            lock_guard<std::mutex> lock(_mtx);
            sql::SQLString SQL = "select cpu_limit from QuestionBank where id = ?";
            unique_ptr<sql::ResultSet> rst = PrepareExecuteSQL(id, SQL);
            unsigned int ret;
            while (rst->next())
            {
                ret = rst->getInt("cpu_limit");
            }
            return ret;
        }

    private:
        sql::Connection *_con;

        // 数据库本身就是一个共享资源，做保护
        std::mutex _mtx;
    };
}